gdk: Make window moves a custom vfunc
authorBenjamin Otte <otte@redhat.com>
Wed, 21 Jul 2010 18:37:33 +0000 (20:37 +0200)
committerBenjamin Otte <otte@redhat.com>
Tue, 10 Aug 2010 19:02:28 +0000 (21:02 +0200)
The window move code needs special attention for multiple reasons:
- invalid areas for expose events need to be modified
- self-copy is not supported by Cairo
- in X11, copying from an overlapped Window might cause unexposed areas
  to be copied in, spo expose events for those need to be generated.

This was all special cased in various parts of the code. By making it an
explicit vfunc, we can work around it.

gdk/gdkoffscreenwindow.c
gdk/gdkwindow.c
gdk/gdkwindowimpl.h
gdk/quartz/gdkgeometry-quartz.c
gdk/quartz/gdkwindow-quartz.c
gdk/win32/gdkwindow-win32.c
gdk/x11/gdkgeometry-x11.c
gdk/x11/gdkprivate-x11.h
gdk/x11/gdkwindow-x11.c

index a21ad163a0097b4e2f7b4c88bad8ed78711ddce4..b1c068b15610d9148bb1a4677e6f0ef035e458c6 100644 (file)
@@ -832,12 +832,38 @@ gdk_offscreen_window_queue_antiexpose (GdkWindow *window,
 }
 
 static void
-gdk_offscreen_window_queue_translation (GdkWindow *window,
-                                       GdkGC     *gc,
-                                       cairo_region_t *area,
-                                       gint       dx,
-                                       gint       dy)
+gdk_offscreen_window_translate (GdkWindow      *window,
+                                cairo_region_t *area,
+                                gint            dx,
+                                gint            dy)
 {
+  cairo_surface_t *surface;
+  cairo_t *cr;
+
+  /* Can't use gdk_cairo_create here due to clipping */
+  surface = _gdk_drawable_ref_cairo_surface (window);
+  cr = cairo_create (surface);
+  cairo_surface_destroy (surface);
+
+  area = cairo_region_copy (area);
+
+  gdk_cairo_region (cr, area);
+  cairo_clip (cr);
+  
+  /* NB: This is a self-copy and Cairo doesn't support that yet.
+   * So we do a litle trick.
+   */
+  cairo_push_group (cr);
+
+  gdk_cairo_set_source_pixmap (cr, window, dx, dy);
+  cairo_paint (cr);
+
+  cairo_pop_group_to_source (cr);
+  cairo_paint (cr);
+
+  cairo_destroy (cr);
+
+  _gdk_window_add_damage (window, area);
 }
 
 /**
@@ -950,7 +976,7 @@ gdk_offscreen_window_impl_iface_init (GdkWindowImplIface *iface)
   iface->input_shape_combine_region = gdk_offscreen_window_input_shape_combine_region;
   iface->set_static_gravities = gdk_offscreen_window_set_static_gravities;
   iface->queue_antiexpose = gdk_offscreen_window_queue_antiexpose;
-  iface->queue_translation = gdk_offscreen_window_queue_translation;
+  iface->translate = gdk_offscreen_window_translate;
   iface->get_root_coords = gdk_offscreen_window_get_root_coords;
   iface->get_deskrelative_origin = gdk_offscreen_window_get_deskrelative_origin;
   iface->get_device_state = gdk_offscreen_window_get_device_state;
index 91854a3f605f20365fe4ce931e00c14ead2db525..9cd022f4dd270fe9dc0d2e78fa43c82ce3e08f7a 100644 (file)
@@ -3223,45 +3223,11 @@ do_move_region_bits_on_impl (GdkWindowObject *impl_window,
                             cairo_region_t *dest_region, /* In impl window coords */
                             int dx, int dy)
 {
-  GdkGC *tmp_gc;
-  GdkRectangle copy_rect;
-  GdkWindowObject *private;
   GdkWindowImplIface *impl_iface;
 
-  /* We need to get data from subwindows here, because we might have
-   * shaped a native window over the moving region (with bg none,
-   * so the pixels are still there). In fact we might need to get data
-   * from overlapping native window that are not children of this window,
-   * so we copy from the toplevel with INCLUDE_INFERIORS.
-   */
-  private = impl_window;
-  while (!gdk_window_is_toplevel (private))
-    {
-      dx -= private->parent->abs_x + private->x;
-      dy -= private->parent->abs_y + private->y;
-      private = gdk_window_get_impl_window (private->parent);
-    }
-  tmp_gc = _gdk_drawable_get_subwindow_scratch_gc ((GdkWindow *)private);
-
-  cairo_region_get_extents (dest_region, &copy_rect);
-  gdk_gc_set_clip_region (tmp_gc, dest_region);
-
-  /* The region area is moved and we queue translations for all expose events
-     to the source area that were sent prior to the copy */
-  cairo_region_translate (dest_region, -dx, -dy); /* Move to source region */
-  impl_iface = GDK_WINDOW_IMPL_GET_IFACE (private->impl);
-
-  impl_iface->queue_translation ((GdkWindow *)impl_window,
-                                tmp_gc,
-                                dest_region, dx, dy);
+  impl_iface = GDK_WINDOW_IMPL_GET_IFACE (impl_window->impl);
 
-  gdk_draw_drawable (impl_window->impl,
-                    tmp_gc,
-                    private->impl,
-                    copy_rect.x-dx, copy_rect.y-dy,
-                    copy_rect.x, copy_rect.y,
-                    copy_rect.width, copy_rect.height);
-  gdk_gc_set_clip_region (tmp_gc, NULL);
+  impl_iface->translate ((GdkWindow *) impl_window, dest_region, dx, dy);
 }
 
 static GdkWindowRegionMove *
@@ -3271,7 +3237,7 @@ gdk_window_region_move_new (cairo_region_t *region,
   GdkWindowRegionMove *move;
 
   move = g_slice_new (GdkWindowRegionMove);
-  move->dest_region  = cairo_region_copy (region);
+  move->dest_region = cairo_region_copy (region);
   move->dx = dx;
   move->dy = dy;
 
index 089706f31e8f414ca04f248ada0cdc39bbf7f7b4..a01244e5ad1f21b16a14ca69bc0a22507a71907e 100644 (file)
@@ -121,10 +121,15 @@ struct _GdkWindowImplIface
    * for destroying the region later.
    */
   gboolean     (* queue_antiexpose)     (GdkWindow       *window,
-                                        cairo_region_t       *update_area);
-  void         (* queue_translation)    (GdkWindow       *window,
-                                        GdkGC           *gc,
-                                        cairo_region_t       *area,
+                                        cairo_region_t  *update_area);
+
+  /* Called to move @area inside @window by @dx x @dy pixels. @area is 
+   * guaranteed to be inside @window. If part of @area is not invisible or
+   * invalid, it is this function's job to queue expose events in those 
+   * areas.
+   */
+  void         (* translate)            (GdkWindow       *window,
+                                        cairo_region_t  *area,
                                         gint            dx,
                                         gint            dy);
 
index 9881cd71b18c4450509d64e50d3888720db12624..bde84aad94302e7c143cd07b036cfe5f00e7a9cb 100644 (file)
 
 #include "gdkprivate-quartz.h"
 
+/* FIXME: Tis function has never been compiled.
+ * Please make it work. */
 void
-_gdk_quartz_window_queue_translation (GdkWindow *window,
-                                     GdkGC     *gc,
-                                      cairo_region_t *area,
-                                      gint       dx,
-                                      gint       dy)
+_gdk_quartz_window_translate (GdkWindow      *window,
+                              cairo_region_t *area,
+                              gint            dx,
+                              gint            dy)
 {
   GdkWindowObject *private = (GdkWindowObject *)window;
   GdkWindowImplQuartz *impl = (GdkWindowImplQuartz *)private->impl;
+  GdkRectangle extents;
 
-  int i, n_rects;
-  cairo_region_t *intersection;
-  GdkRectangle rect;
+  cairo_region_get_extents (area, &extents);
 
-  /* We will intersect the known region that needs display with the given
-   * area.  This intersection will be translated by dx, dy.  For the end
-   * result, we will also set that it needs display.
-   */
+  [window_impl->view scrollRect:NSMakeRect (extents.x, extents.y, extents.width, extents.height)
+                             by:NSMakeSize (dx, dy)];
 
-  if (!impl->needs_display_region)
-    return;
+  if (impl->needs_display_region)
+    {
+      intersection = cairo_region_copy (impl->needs_display_region);
+      cairo_region_intersect_rectangle (intersection, extents);
+      cairo_region_translate (intersection, dx, dy);
 
-  intersection = cairo_region_copy (impl->needs_display_region);
-  cairo_region_intersect (intersection, area);
-  cairo_region_translate (intersection, dx, dy);
-
-  _gdk_quartz_window_set_needs_display_in_region (window, intersection);
-  cairo_region_destroy (intersection);
+      _gdk_quartz_window_set_needs_display_in_region (window, intersection);
+      cairo_region_destroy (intersection);
+    }
 }
 
 gboolean
index 815a3b5a4f43f2f9b0c2a55f361e1772c06480a0..6e72baa768fa7af8c26fdfcc5a530bc085e2f09a 100644 (file)
@@ -3102,6 +3102,6 @@ gdk_window_impl_iface_init (GdkWindowImplIface *iface)
   iface->input_shape_combine_region = gdk_window_quartz_input_shape_combine_region;
   iface->set_static_gravities = gdk_window_quartz_set_static_gravities;
   iface->queue_antiexpose = _gdk_quartz_window_queue_antiexpose;
-  iface->queue_translation = _gdk_quartz_window_queue_translation;
+  iface->translate = _gdk_quartz_window_translate;
   iface->destroy = _gdk_quartz_window_destroy;
 }
index 53676b736459724a5347d70f3c43df77770d93f9..3d5e4e725b4900b60b85614b4a52f9cfbb1f100d 100644 (file)
@@ -3282,25 +3282,27 @@ _gdk_win32_window_queue_antiexpose (GdkWindow *window,
   return FALSE;
 }
 
-/*
- * queue_translation is meant to only move any outstanding invalid area
- * in the given area by dx,dy. A typical example of when its needed is an
- * app with two toplevels where one (A) overlaps the other (B). If the
- * app first moves A so that B is invalidated and then scrolls B before
- * handling the expose. The scroll operation will copy the invalid area
- * to a new position, but when the invalid area is then exposed it only
- * redraws the old areas not the place where the invalid data was copied
- * by the scroll.
- */
+/* FIXME: Tis function has never been compiled.
+ * Please make it work. */
 static void
-_gdk_win32_window_queue_translation (GdkWindow *window,
-                                    GdkGC     *gc,
-                                    cairo_region_t *area,
-                                    gint       dx,
-                                    gint       dy)
+_gdk_win32_window_translate (GdkWindow *window,
+                             cairo_region_t *area,
+                             gint       dx,
+                             gint       dy)
 {
   HRGN hrgn = CreateRectRgn (0, 0, 0, 0);
-  int ret = GetUpdateRgn (GDK_WINDOW_HWND (window), hrgn, FALSE);
+  HDC hdc;
+  int ret;
+  GdkRectangle extents;
+
+  cairo_region_get_extents (area, &extents);
+  hdc = _gdk_win32_drawable_acquire_dc (GDK_DRAWABLE (window));
+  GDI_CALL (BitBlt, (hdc, extents.x, extents.y, extents.width, extents.height,
+                    hdc, extents.x + dx, extents.y + dy, SRCCOPY));
+
+  /* XXX: We probably need to get invalidations for the whole extents and not
+   * just the area as we BitBlt */
+  ret = GetUpdateRgn (GDK_WINDOW_HWND (window), hrgn, FALSE);
   if (ret == ERROR)
     WIN32_API_FAILED ("GetUpdateRgn");
   else if (ret != NULLREGION)
@@ -3374,7 +3376,7 @@ gdk_window_impl_iface_init (GdkWindowImplIface *iface)
   iface->get_deskrelative_origin = gdk_win32_window_get_deskrelative_origin;
   iface->set_static_gravities = gdk_win32_window_set_static_gravities;
   iface->queue_antiexpose = _gdk_win32_window_queue_antiexpose;
-  iface->queue_translation = _gdk_win32_window_queue_translation;
+  iface->translate = _gdk_win32_window_translate;
   iface->destroy = _gdk_win32_window_destroy;
 }
 
index 83a899561ca4b8e64dd4c788d2b528af56b1c360..4ddf5b422b392fb8541ccc9dff8f124b82043762 100644 (file)
@@ -226,24 +226,62 @@ gdk_window_queue (GdkWindow          *window,
 }
 
 void
-_gdk_x11_window_queue_translation (GdkWindow *window,
-                                  GdkGC     *gc,
-                                  cairo_region_t *area,
-                                  gint       dx,
-                                  gint       dy)
+_gdk_x11_window_translate (GdkWindow      *window,
+                           cairo_region_t *area,
+                           gint            dx,
+                           gint            dy)
 {
-  GdkWindowQueueItem *item = g_new (GdkWindowQueueItem, 1);
-  item->type = GDK_WINDOW_QUEUE_TRANSLATE;
-  item->u.translate.area = area ? cairo_region_copy (area) : NULL;
-  item->u.translate.dx = dx;
-  item->u.translate.dy = dy;
+  GdkWindowQueueItem *item;
+  GdkGC *tmp_gc;
+  GdkRectangle extents;
+  GdkWindowObject *private, *impl;
+  int px, py;
+
+  /* We need to get data from subwindows here, because we might have
+   * shaped a native window over the moving region (with bg none,
+   * so the pixels are still there). In fact we might need to get data
+   * from overlapping native window that are not children of this window,
+   * so we copy from the toplevel with INCLUDE_INFERIORS.
+   */
+  private = impl = (GdkWindowObject *) window;
+  px = py = 0;
+  while (private->parent != NULL &&
+         private->parent->window_type != GDK_WINDOW_ROOT)
+    {
+      dx -= private->parent->abs_x + private->x;
+      dy -= private->parent->abs_y + private->y;
+      private = (GdkWindowObject *) _gdk_window_get_impl_window ((GdkWindow *) private->parent);
+    }
+
+  cairo_region_get_extents (area, &extents);
+
+  tmp_gc = _gdk_drawable_get_subwindow_scratch_gc ((GdkWindow *)private);
+  gdk_gc_set_clip_region (tmp_gc, area);
+
+  cairo_region_translate (area, -dx, -dy); /* Move to source region */
 
   /* Ensure that the gc is flushed so that we get the right
      serial from NextRequest in gdk_window_queue, i.e. the
      the serial for the XCopyArea, not the ones from flushing
      the gc. */
-  _gdk_x11_gc_flush (gc);
+  _gdk_x11_gc_flush (tmp_gc);
+
+  item = g_new (GdkWindowQueueItem, 1);
+  item->type = GDK_WINDOW_QUEUE_TRANSLATE;
+  item->u.translate.area = cairo_region_copy (area);
+  item->u.translate.dx = dx;
+  item->u.translate.dy = dy;
   gdk_window_queue (window, item);
+
+  XCopyArea (GDK_WINDOW_XDISPLAY (impl),
+             GDK_DRAWABLE_IMPL_X11 (private->impl)->xid,
+             GDK_DRAWABLE_IMPL_X11 (impl->impl)->xid,
+             GDK_GC_GET_XGC (tmp_gc),
+             extents.x - dx, extents.y - dy,
+             extents.width, extents.height,
+             extents.x, extents.y);
+
+  gdk_gc_set_clip_region (tmp_gc, NULL);
 }
 
 gboolean
index e536f867d7c2b87c94bb95c0359bfdf3cd5264e1..236164fc49494ae594502af74f2b164429f678fe 100644 (file)
@@ -118,8 +118,7 @@ void _gdk_window_process_expose    (GdkWindow     *window,
 
 gboolean _gdk_x11_window_queue_antiexpose  (GdkWindow *window,
                                            cairo_region_t *area);
-void     _gdk_x11_window_queue_translation (GdkWindow *window,
-                                           GdkGC     *gc,
+void     _gdk_x11_window_translate         (GdkWindow *window,
                                            cairo_region_t *area,
                                            gint       dx,
                                            gint       dy);
index 30620077b8cd956107cb761555cc4c6a4668107b..1339b7bbb4e974421fcff61ebe2e69528856300f 100644 (file)
@@ -5562,7 +5562,7 @@ gdk_window_impl_iface_init (GdkWindowImplIface *iface)
   iface->input_shape_combine_region = gdk_window_x11_input_shape_combine_region;
   iface->set_static_gravities = gdk_window_x11_set_static_gravities;
   iface->queue_antiexpose = _gdk_x11_window_queue_antiexpose;
-  iface->queue_translation = _gdk_x11_window_queue_translation;
+  iface->translate = _gdk_x11_window_translate;
   iface->destroy = _gdk_x11_window_destroy;
   iface->supports_native_bg = TRUE;
 }